#include <jni.h>
#include <string>
#include <android/log.h>
#include <unistd.h>
#include <thread>
#include <MediaPlay.h>
#include <SimplePlayer.h>
#include <SimpleRecord.h>

#include <HyhPlayer.h>
#include <VideoRecord.h>
#include <AudioRecord.h>
#include "video.h"


extern "C" {
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <libswresample/swresample.h>
}

#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"nativeLog",FORMAT,##__VA_ARGS__);

static JavaVM * javaVM;
jobject object;
SimpleRecord simpleRecord;
VideoRecord videoRecord;
AudioRecord audioRecord;

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_MusicPlay_sound(JNIEnv *env, jobject instance, jstring input_) {
    const char *input = env->GetStringUTFChars(input_, 0);

    env->ReleaseStringUTFChars(input_, input);
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_MusicPlay_start(JNIEnv *env, jobject instance, jstring filePath_,
                                       jobject surface) {
    const char *filePath = env->GetStringUTFChars(filePath_, 0);
    env->GetJavaVM(&javaVM);
    object = env->NewGlobalRef(instance);
//    MediaPlay mediaPlay;
    ANativeWindow *aNativeWindow = ANativeWindow_fromSurface(env,surface);
//    mediaPlay.setDataSource(javaVM,object,filePath,aNativeWindow);
    SimplePlay simplePlay;
    simplePlay.open(javaVM,object,filePath,aNativeWindow);
    env->ReleaseStringUTFChars(filePath_, filePath);
}


extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_MediaPlay_start(JNIEnv *env, jobject instance, jstring filePath_,
                                       jobject surface) {
    const char *filePath = env->GetStringUTFChars(filePath_, 0);
    av_register_all();
    AVFormatContext *pAVFormatCtx=avformat_alloc_context();
    if(avformat_open_input(&pAVFormatCtx, filePath, NULL, NULL)!=0){
        LOGE("读取头信息失败");
        return;
    }else{
        LOGE("视频播放时长%d",pAVFormatCtx->duration);
    }
    if(avformat_find_stream_info(pAVFormatCtx,NULL)<0){
        LOGE("读取视频内容失败");
        return;
    } else{
        LOGE("视频播放时长%d",pAVFormatCtx->duration);
    }
    int videoStream=-1;
    int audioStream=-1;
    for(int i=0;i<pAVFormatCtx->nb_streams;i++){
        if(pAVFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
            videoStream = i;
        }else if(pAVFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
            audioStream = i;
        }
    }
    if(videoStream==-1){
        LOGE("获取视频流失败");
        return;
    }
    if(audioStream==-1){
        LOGE("获取音频流失败");
        return;
    }
    AVCodecContext * pAVVideoCodecCtx = pAVFormatCtx->streams[videoStream]->codec;
    AVCodecContext * pAVAudioCodecCtx = pAVFormatCtx->streams[audioStream]->codec;
    AVCodec * pAVVideoCodec = avcodec_find_decoder(pAVVideoCodecCtx->codec_id);
    AVCodec * pAVAudioCodec = avcodec_find_decoder(pAVAudioCodecCtx->codec_id);

    if(pAVVideoCodec==NULL){
        LOGE("获取视频流解码库失败");
        return;
    }

    if(pAVAudioCodec==NULL){
        LOGE("获取视频流解码库失败");
        return;
    }

    if(avcodec_open2(pAVVideoCodecCtx,pAVVideoCodec,NULL)!=0){
        LOGE("打开视频流解码库失败");
        return;
    }

    if(avcodec_open2(pAVAudioCodecCtx,pAVAudioCodec,NULL)!=0){
        LOGE("打开音频流解码库失败");
        return;
    }

    //frame->16bit 44100 PCM 统一音频采样格式与采样率
    SwrContext *swrCtx = swr_alloc();
    //重采样设置选项-----------------------------------------------------------start
    //输入的采样格式
    enum AVSampleFormat in_sample_fmt = pAVAudioCodecCtx->sample_fmt;
    //输出的采样格式 16bit PCM
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    //输入的采样率
    int in_sample_rate = pAVAudioCodecCtx->sample_rate;
    //输出的采样率
    int out_sample_rate = 44100;
    //输入的声道布局
    uint64_t in_ch_layout = pAVAudioCodecCtx->channel_layout;
    //输出的声道布局
    uint64_t out_ch_layout = AV_CH_LAYOUT_MONO;

    swr_alloc_set_opts(swrCtx, out_ch_layout, out_sample_fmt, out_sample_rate, in_ch_layout, in_sample_fmt,
                       in_sample_rate, 0, NULL);
    swr_init(swrCtx);
    //重采样设置选项-----------------------------------------------------------end
    int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);

    SwsContext *swsContext = sws_getContext(pAVVideoCodecCtx->width,pAVVideoCodecCtx->height,pAVVideoCodecCtx->pix_fmt,
                                            pAVVideoCodecCtx->width,pAVVideoCodecCtx->height,AV_PIX_FMT_RGBA,SWS_BILINEAR,NULL,NULL,NULL);
    int outSize = av_image_get_buffer_size(AV_PIX_FMT_RGBA,pAVVideoCodecCtx->width,pAVVideoCodecCtx->height,1);
    uint8_t *outBuffer= (uint8_t *) malloc(sizeof(uint8_t) * outSize);
    AVFrame *pAVFrame=av_frame_alloc();
    AVFrame *pRGBFrame=av_frame_alloc();
    av_image_fill_arrays(pRGBFrame->data,pRGBFrame->linesize,outBuffer,AV_PIX_FMT_RGBA,
                         pAVVideoCodecCtx->width,pAVVideoCodecCtx->height,1);
    ANativeWindow * pANativeWindow = ANativeWindow_fromSurface(env,surface);
    ANativeWindow_setBuffersGeometry(pANativeWindow,pAVVideoCodecCtx->width,pAVVideoCodecCtx->height,WINDOW_FORMAT_RGBA_8888);

    ANativeWindow_Buffer buffer;
    int gotFrame,frameCount=0;
    AVPacket *pAVPacket= (AVPacket *) malloc(sizeof(AVPacket));
    while(av_read_frame(pAVFormatCtx,pAVPacket)>=0){
        if(pAVPacket->stream_index==videoStream){
            avcodec_decode_video2(pAVVideoCodecCtx,pAVFrame,&gotFrame,pAVPacket);
            if(gotFrame){
                sws_scale(swsContext, (const uint8_t *const *) pAVFrame->data, pAVFrame->linesize,
                          0, pAVFrame->height, pRGBFrame->data, pRGBFrame->linesize);

                ANativeWindow_lock(pANativeWindow,&buffer,NULL);
                uint8_t *firstWindow = (uint8_t *) buffer.bits;
                uint8_t *firstRgbFrame = pRGBFrame->data[0];
                int windowLineByte = buffer.stride*4;
                int rgbLineByte = pRGBFrame->linesize[0];
                for(int i=0;i<pAVVideoCodecCtx->height;i++){
                    memcpy(firstWindow+i*windowLineByte,firstRgbFrame+rgbLineByte*i,rgbLineByte);
                }
                ANativeWindow_unlockAndPost(pANativeWindow);
                LOGE("width=%d;height=%d;avlinesize=%d;rgblinesize=%d;gotFrame=%d",pAVFrame->width,pAVFrame->height,pAVFrame->linesize[0],pRGBFrame->linesize[0],gotFrame);
            }
        } else if(pAVPacket->stream_index==audioStream){

        }
        av_free_packet(pAVPacket);
    }
    env->ReleaseStringUTFChars(filePath_, filePath);
}



extern "C"
JNIEXPORT jstring JNICALL
Java_hyh_com_videodemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    av_register_all();
    return env->NewStringUTF(hello.c_str());
}


HyhPlayer hyhPlayer;

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_SimplePlayer_start(JNIEnv *env, jobject thiz, jstring file_path,
                                          jobject surface) {
    const char * filePath = env->GetStringUTFChars(file_path,0);
    env->GetJavaVM(&javaVM);
    object = env->NewGlobalRef(thiz);
    ANativeWindow *aNativeWindow = ANativeWindow_fromSurface(env,surface);
    hyhPlayer.open(javaVM,object,filePath,aNativeWindow);
    env->ReleaseStringUTFChars(file_path,filePath);
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_SimplePlayer_putData(JNIEnv *env, jobject thiz, jbyteArray data, jint len) {

    jbyte * bytes = env->GetByteArrayElements(data,NULL);
    hyhPlayer.putData((uint8_t *)bytes,len);
    env->ReleaseByteArrayElements(data,bytes,0);
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_SimplePlayer_stop(JNIEnv *env, jobject thiz) {
    hyhPlayer.close();
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_VideoRecorder_initAudio(JNIEnv *env, jobject thiz, jstring dest_url) {
    const char *destUrl = env->GetStringUTFChars(dest_url, 0);
    audioRecord.open(destUrl);
    env->ReleaseStringUTFChars(dest_url,destUrl);
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_VideoRecorder_initVideo(JNIEnv *env, jobject thiz, jstring dest_url,
                                               jint width, jint height) {
    LOGE("Java_hyh_com_videodemo_VideoRecorder_init");
    const char *destUrl = env->GetStringUTFChars(dest_url, 0);
    videoRecord.init(destUrl,width,height);
    env->ReleaseStringUTFChars(dest_url,destUrl);
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_VideoRecorder_encodeVideo(JNIEnv *env, jobject thiz, jbyteArray data,
                                                 jint len) {
    jbyte *bytes = env->GetByteArrayElements(data, NULL);
    simpleRecord.encodeVideo((uint8_t *) bytes,len);
    env->ReleaseByteArrayElements(data, bytes, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_VideoRecorder_encodeAudio(JNIEnv *env, jobject thiz, jbyteArray data,
                                                 jint len) {
    jbyte *bytes = env->GetByteArrayElements(data, NULL);
    simpleRecord.encodeAudio((uint8_t *) bytes,len);
//     audioRecord.encode((uint8_t *) bytes,len);
    env->ReleaseByteArrayElements(data, bytes, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_VideoRecorder_stopVideo(JNIEnv *env, jobject thiz) {
    videoRecord.stop();
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_VideoRecorder_stopAudio(JNIEnv *env, jobject thiz) {
    audioRecord.stop();
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_VideoRecorder_open(JNIEnv *env, jobject thiz, jstring dest_url, jint width,
                                          jint height) {
    const char *destUrl = env->GetStringUTFChars(dest_url, 0);
    env->GetJavaVM(&javaVM);
    object = env->NewGlobalRef(thiz);
    simpleRecord.open(javaVM,object,destUrl,width,height);
    env->ReleaseStringUTFChars(dest_url,destUrl);
}

extern "C"
JNIEXPORT void JNICALL
Java_hyh_com_videodemo_VideoRecorder_stop(JNIEnv *env, jobject thiz) {
    simpleRecord.stop();
}

